<html>
    <head>
		<title>FreeMove</title>
		<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
        <script type="text/javascript" src="jquery3.1.js"></script>
        <script type="text/javascript" src="vconsole.min.js"></script>
        <style>
            .test {
                width:100px;
                height:100px;
                position:absolute;
                left:0;
                top:0;
                background: gray;
            }
        </style>
        <script>
			~function ($) {
				function _eventNamespace(flag) {
					return 'freeMove_' + flag;
				}

				$.fn.freeMove = function (options, flag, selector, data) {
					if(this.length <= 0) {
						return ;
					}

					var defopt = $.extend({}, options);

					if(typeof flag != 'undefined') {
						defopt.flag = options.flag = flag;
					} else {
						if(typeof options.flag == 'undefined') {
							options.flag = String(new Date().getTime() + '_' + Math.random()).replace(/\./g, '_');
						}

						flag = defopt.flag = options.flag
					}

					/*release*/
					if(typeof window.freeMoveElements == 'undefined') {
						window.freeMoveElements = [];
                        var eventNamespace = _eventNamespace(flag);
						var events = [
							'mouseup.' + eventNamespace,
							'touchend.' + eventNamespace,
							'touchcancel.' + eventNamespace
						];

                        /* mouseup */
						$(document).on(events.join(' '), selector, data, function (e) {
							for(var i=0; i<window.freeMoveElements.length; i++) {
								var elm = window.freeMoveElements[i].elm;

								if(!elm.freeMoveData[flag].data.moving) {
									continue;
								}

								elm.freeMoveData[flag].data.moving = false;

								if(typeof elm.freeMoveData[flag].options.end == 'function') {
									elm.freeMoveData[flag].options.end.apply(elm, [e])
								}
                            }
						});

						events = [
							'mousemove.' + eventNamespace,
							'touchmove.' + eventNamespace
						];
                        /* moving */
						$(window).on(events.join(' '), function (e) {
							e.preventDefault();
							e.stopPropagation();

							for(var i=0; i<window.freeMoveElements.length; i++) {
								var elm = window.freeMoveElements[i].elm;

								if (!elm.freeMoveData[flag].data.moving || elm.freeMoveData[flag].data.cancel) {
									continue;
								}

								var args = {};

								if(e.type.toLowerCase().substr(0, "touch".length) == "touch") {
									args.x = e.targetTouches[0].pageX - elm.freeMoveData[flag].data.lastX;
									args.y = e.targetTouches[0].pageY - elm.freeMoveData[flag].data.lastY;
									elm.freeMoveData[flag].data.lastX = e.targetTouches[0].pageX;
									elm.freeMoveData[flag].data.lastY = e.targetTouches[0].pageY;
								} else {
									args.x = e.pageX - elm.freeMoveData[flag].data.lastX;
									args.y = e.pageY - elm.freeMoveData[flag].data.lastY;
									elm.freeMoveData[flag].data.lastX = e.pageX;
									elm.freeMoveData[flag].data.lastY = e.pageY;
								}

								if (typeof elm.freeMoveData[flag].options.moving == 'function') {
									if (false === elm.freeMoveData[flag].options.moving.apply(elm, [args, e])) {
										elm.freeMoveData[flag].data.cancel = true;
									}
								}
							}
						});
                    }

					if(typeof options == "function") {
						defopt.moving = options;
					}

					this.each(function () {
						var elm = $(this)[0];
						if(typeof elm.freeMoveData == 'undefined') {
							elm.freeMoveData = {};
						}

						if(elm.freeMoveData[flag]) {
							console && console.warn('$.fn.freeMove: \'' + flag + '\' is alreadly exsits');
							return;
						}

						elm.freeMoveData[flag] = {'options' : defopt, 'data':{}};
						window.freeMoveElements.push({'options' : defopt, elm: elm});

						_initEvent(elm, selector, data);
					});

					function _initEvent(elm, selector, data) {
						var eventNamespace = _eventNamespace(flag);
						var events = [
							'mousedown.' + eventNamespace,
							'touchstart.' + eventNamespace
						];
                        /* mousedown */
						$(elm).on(events.join(' '), function (e) {
							if(typeof elm.freeMoveData[flag].options.begin == 'function') {
								if(false === elm.freeMoveData[flag].options.begin.apply(elm, [e])) {
									return ;
								}
							}

							if(e.type.toLowerCase().substr(0, "touch".length) == "touch") {
								elm.freeMoveData[flag].data.lastX = e.targetTouches[0].pageX;
								elm.freeMoveData[flag].data.lastY = e.targetTouches[0].pageY;
                            } else {
								elm.freeMoveData[flag].data.lastX = e.pageX;
								elm.freeMoveData[flag].data.lastY = e.pageY;
                            }

							elm.freeMoveData[flag].data.moving = true;
							elm.freeMoveData[flag].data.cancel = false;
						});

						events = [
							'mouseup.' + eventNamespace,
							'touchend.' + eventNamespace,
							'touchcancel.' + eventNamespace
						];

                        /* mouseup */
						$(elm).on(events.join(' '), function (e) {
							if(!elm.freeMoveData[flag].data.moving) {
								return;
							}

							elm.freeMoveData[flag].data.moving = false;

							if(typeof elm.freeMoveData[flag].options.end == 'function') {
								elm.freeMoveData[flag].options.end.apply(elm, [e])
							}
						});

						events = [
							'mousemove.' + eventNamespace,
							'touchmove.' + eventNamespace
						];
                        /* moving */
						$(elm).on(events.join(' '), function (e) {
							if(!elm.freeMoveData[flag].data.moving || elm.freeMoveData[flag].data.cancel) {
								return;
							}

							var args = {};

							if(e.type.toLowerCase().substr(0, "touch".length) == "touch") {
								args.x = e.targetTouches[0].pageX - elm.freeMoveData[flag].data.lastX;
								args.y = e.targetTouches[0].pageY - elm.freeMoveData[flag].data.lastY;
								elm.freeMoveData[flag].data.lastX = e.targetTouches[0].pageX;
								elm.freeMoveData[flag].data.lastY = e.targetTouches[0].pageY;
							} else {
								args.x = e.pageX - elm.freeMoveData[flag].data.lastX;
								args.y = e.pageY - elm.freeMoveData[flag].data.lastY;
								elm.freeMoveData[flag].data.lastX = e.pageX;
								elm.freeMoveData[flag].data.lastY = e.pageY;
                            }

							if(typeof elm.freeMoveData[flag].options.moving == 'function') {
								e.preventDefault();
								e.stopPropagation();

								if(false === elm.freeMoveData[flag].options.moving.apply(elm, [args, e])) {
									elm.freeMoveData[flag].data.cancel = true;
								}
							}
						});
					}
				}; /* freeMove */

				$.fn.unfreeMove = function(flag, selector) {
					if(this.length <= 0) {
						return ;
					}

					var eventNamespace = _eventNamespace(flag);

					this.each(function () {
						var elm = $(this)[0];

						if(typeof elm.freeMoveData == 'undefined') {
							elm.freeMoveData = {};
						}

						if(elm.freeMoveData[flag]) {
							var events = [
								'mousedown.' + eventNamespace,
								'touchstart.' + eventNamespace,
								'mouseup.' + eventNamespace,
								'touchend.' + eventNamespace,
								'touchcancel.' + eventNamespace,
								'mousemove.' + eventNamespace,
								'touchmove.' + eventNamespace

							];

							$(elm).off(events.join(' '), selector);
							delete elm.freeMoveData[flag];

							for(var i=0; i<window.freeMoveElements.length; i++) {
								if(window.freeMoveElements[i].options.flag == flag && window.freeMoveElements[i].elm == elm) {
									window.freeMoveElements.splice(i, 1);
									i--;
                                }
                            }
						}
					});
				}

			}(jQuery);
        </script>
    </head>
    <body>
        <div class="test"></div>
        <script>
            $('.test').freeMove({begin:function(){
                    console.log('begin');
			    },

                moving: function (pos) {
					var top = $(this)[0].offsetTop;
					var left = $(this)[0].offsetLeft;

                $(this).css({left:left + pos.x, top: top+pos.y});
//					$(this).css({top: top+pos.y});
				},

                end: function () {
					console.log('end');
				}
            });
        </script>
    </body>
</html>